package com.zhny.sensor.nio;

import com.zhny.sensor.entity.*;
import com.zhny.sensor.service.*;
import com.zhny.sensor.utils.*;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import org.apache.commons.codec.CharEncoding;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Auther: zhy
 * @Date: 2019/11/14
 * @Description: 设备功能服务
 */
@Service
@ChannelHandler.Sharable // 可以被多次调用，被装配到多个pipline中去
public class ServerHandler extends ChannelInboundHandlerAdapter {

    @Autowired
    private UserService userService;

    @Autowired
    private IdWorker idWorker;

    @Autowired
    private BarcodeMachineService barcodeMachineService;

    @Autowired
    private BarcodeMachineUserService barcodeMachineUserService;

    @Autowired
    private PoultryCategoryService poultryCategoryService;

    @Autowired
    private SlaughterHousePoultryService slaughterHousePoultryService;

    @Autowired
    private BarcodeMachineStatusService barcodeMachineStatusService;

    @Autowired
    private BarcodeMachineAlertservice barcodeMachineAlertservice;

    @Autowired
    private LabelingRecordService labelingRecordService;

    @Autowired
    private ProgramVersionService programVersionService;

    @Value("${firmware.filepath}")
    private String firmwareFilePath;

    @Value("${datareport.cycle}")
    private Byte dataReportCycle;

    @Value("${connection.limits}")
    private int CONNECTIONLIMITS;

    private static final Logger log = LoggerFactory.getLogger(ServerHandler.class);


    private static AtomicInteger connections = new AtomicInteger(0);


    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        super.channelInactive(ctx);
        connections.getAndDecrement();
        log.debug(Thread.currentThread().getId() + "当前的连接数为:"+connections.get());
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
        String ip = insocket.getAddress().getHostAddress();
        int port = insocket.getPort();
        log.info(Thread.currentThread().getId() + "已经连接，连接的ip为  " + ip + "," + port);
        if (connections.getAndAdd(1) >= CONNECTIONLIMITS) {//线程问题
            ctx.close();
            log.error(Thread.currentThread().getId() + "因为连接数已满，断开了这个连接");
            return;
        }
        log.debug(Thread.currentThread().getId() + "当前的连接数为:"+connections.get());
        super.channelActive(ctx);
    }

    /**
     * 服务端接受到客户端数据后被调用
     *  待完善的地方：可以自定义一个线程池，用来处理业务的逻辑
     * @param ctx
     * @param msg 客户端数据 ByteBuf
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

        // 读取数据
        ByteBuf dataBuf = (ByteBuf) msg;
        int len = dataBuf.readableBytes();
        byte[] dataByte = new byte[len];
        dataBuf.getBytes(dataBuf.readerIndex(), dataByte);

        log.info(Thread.currentThread().getId() + "----收到数据的长度为" + len);
        log.info(Thread.currentThread().getId() + " 接收数据 :[" + ByteUtils.bytesToHexString(dataByte) + "]");

        //ByteBufUtil.hexDump(dataBuf);  用这个速度是会比较快点，不用复制两次，但这个格式不喜欢

        // CRC校验
        if (!checkCRCVal(dataByte)) {
            return;
        }

        //1111111111校验完要再验证数据是否符合协议格式，不符合就丢弃。
        //1111111111每个报错都写进日志里

        // 获取帧代号
        int flag = Integer.parseInt(ByteUtils.byteToHex(dataBuf.getByte(4)), 16);
        log.debug(Thread.currentThread().getId() + "测试-----flag  " + flag);

        // 获取设备编号
        byte[] deviceIdByte = new byte[6];
        dataBuf.getBytes(5, deviceIdByte);
//        String deviceId = Integer.parseInt(ByteBufUtil.hexDump(deviceIdByte),16 ) + "";
//        String deviceId = ByteUtils.bytesToASCIIString(deviceIdByte);
        String deviceId = ByteUtils.bytesToAddress(deviceIdByte);

        log.debug(Thread.currentThread().getId() + "测试-----设备编号  " + deviceId);


        //1111111111111web端在添加设备的时候BarcodeMachine表的id字段和number字段要相同
        // 拿到脚标机的详细信息
        Optional<BarcodeMachine> optionalBarcodeMachine = barcodeMachineService.findById(deviceId);

        //这里是如果查不到设备的信息就直接响应客户端一段数据minxin
        if (!optionalBarcodeMachine.isPresent()) {
            // 响应内容：日期
            LocalDateTime date = LocalDateTime.now();
            String dateHex = DateUtils.datetoHex(date);

            byte[] responseData = ByteUtils.hexStringToBytes(dateHex);
            ByteBuf responseBuf = buildLoginResponsePackage(deviceIdByte, (byte) 0x01, new byte[]{(byte) 0x12, 0x00}, responseData);
            ctx.writeAndFlush(responseBuf);

            log.error(Thread.currentThread().getId() + "该设备未被注册！设备编号：{}", deviceId);
            return;
        }

        //脚标机设备
        BarcodeMachine barcodeMachine = optionalBarcodeMachine.get();

        // 设备登录
        if (flag == 0x01) {
            log.info(Thread.currentThread().getId() + "Option：设备登录，设备编号：{}", deviceId);

            // 响应内容：日期
            LocalDateTime date = LocalDateTime.now();
            String dateHex = DateUtils.datetoHex(date);

            byte[] responseData = ByteUtils.hexStringToBytes(dateHex);

            //获取设备的时间版本编号
            byte[] timeByte = new byte[4];
            dataBuf.getBytes(13, timeByte);
            String timeVersion = ByteUtils.bytesToAddress(timeByte);
            System.out.println("时间版本编号为："+timeVersion);

            //判断信息版本是否需要更新 minxin
            byte[] updateCode = new byte[]{0x00, 0x00};
            //这里需要添加一个标识的符号，如果同时需要更新用户和禽类信息的话 updateCode[1] = 0x03;
            int biaozhi = 0;
            // 用户信息更新请求
            if (hasNewTimeVersion(timeVersion,deviceId)) {
                //将用户信息时间版本的数据存入数据库
//                barcodeMachineUserService.setTimeVersionByMachineId(timeVersion,deviceId);
                updateCode[0] = 0x00;
                updateCode[1] = 0x01;
                log.info(Thread.currentThread().getId() + "用户信息更新请求");
                biaozhi++;
            }
            System.out.println(barcodeMachine.getSlaughterHouseId()+"*******************************");
            // 禽类品种信息更新请求
            if (hasNewPoultryTimeVersion(timeVersion,barcodeMachine.getSlaughterHouseId())) {
                //将用户信息时间版本的数据存入数据库
//                slaughterHousePoultryService.setTimeVersionByMachineId(timeVersion , barcodeMachine.getSlaughterHouseId());
                updateCode[0] = 0x00;
                updateCode[1] = 0x02;
                log.info(Thread.currentThread().getId() + "禽类品种信息更新请求");
                biaozhi++;
            }
            //证明同时需要更新用户和禽类信息
            if(biaozhi==2){
                updateCode[1] = 0x03;
            }
            ByteBuf responseBuf = buildLoginResponsePackage(deviceIdByte, (byte) 0x01, updateCode, responseData);
            ctx.writeAndFlush(responseBuf);
            return;

//
//            // 校验经纬度
//            byte[] longitude = new byte[10];
//            byte[] latitude = new byte[10];
//            dataBuf.getBytes(17,longitude);
//            dataBuf.getBytes(27,latitude);
//
//
//            //1111111这里还欠个登录时检测到设备被禁用的情况
////            if (checkGPS(barcodeMachine, longitude, latitude)) {
//            if (true) {
//
//                byte[] updateCode = new byte[]{0x00,0x00};
//
//                //更新信息如果没有用时间版本管理，将会导致更多管理代价，比如你用isupdate判断后，下次再开机，他还是发现
//                //这个isupdate还是1，然后还是更新。这里主要的难度是，一台设备更新了，但其他设备没有，
//                //所以isupdate还是不能改，只有记录了所有设备都更新了，isupdate才改。
//                //需要比较更新的时间版本
//                //但其实只要记录版本号就可以啊 后期交给你们维护改吧
//
//
//                //1111111111web端改更新状态
//                // 用户信息更新请求
//                if (hasNewUser(deviceId)) {
//                    updateCode[0] = 0x00;
//                    updateCode[1] = 0x01;
//                    log.info(Thread.currentThread().getId()+"用户信息更新请求");
//                }
//
//                //1111111111
//                // 禽类品种信息更新请求
//                if (hasNewPoultry(barcodeMachine.getSlaughterHouseId())) {
//                    updateCode[0] = 0x00;
//                    updateCode[1] = 0x02;
//                    log.info(Thread.currentThread().getId()+"禽类品种信息更新请求");
//                }
//
//                //1111111111
//                // 固件更新请求
//                int firmwareVersion = dataBuf.getByte(11);
//                if (hasNewFirmware(firmwareVersion)) {
//                    updateCode[0] = 0x00;
//                    updateCode[1] = 0x04;
//                    log.info(Thread.currentThread().getId()+"固件更新请求");
//                }
//
//                byte[] responseData = ByteUtils.hexStringToBytes(dateHex);
//                ByteBuf responseBuf = buildLoginResponsePackage(deviceIdByte,(byte) 0x01,updateCode,responseData);
//                ctx.writeAndFlush(responseBuf);
//
//                log.info(Thread.currentThread().getId()+"登录成功! 设备编号：{}",deviceId);
//
//            } else {
//
//                byte[] responseData = ByteUtils.hexStringToBytes(dateHex);
//                ByteBuf responseBuf = buildLoginResponsePackage(deviceIdByte,(byte) 0x01,new byte[]{(byte) 0x11,0x00},responseData);
//                ctx.writeAndFlush(responseBuf);
//
//               log.error(Thread.currentThread().getId()+"登录失败！经纬度验证不通过，超出使用范围！");
//
//            }

        } else if (flag == 0x02) {  // 用户信息更新

            log.info(Thread.currentThread().getId() + " 用户信息更新，设备编号：{}", deviceId);

            // 获取需要更新的用户数据
            BarcodeMachineUser barcodeMachineUser = barcodeMachineUserService.getBarcodeMachineUser(deviceId);

            int updateVersion = Integer.parseInt(barcodeMachineUser.getUpdateVersion());
            List<byte[]> userInfoList = parseUserInfoTobytes(barcodeMachineUser);

            // 创建用户更新信息包
            ByteBuf responseBuf = buildUpdateResponsePackage(deviceIdByte, (byte) 0x02, (short) updateVersion, userInfoList);
            ctx.writeAndFlush(responseBuf);


        } else if (flag == 0x03) {  // 禽类信息更新

            log.info(Thread.currentThread().getId() + "Option：禽类信息更新，设备编号：{}", deviceId);

            // 获取禽类品种信息
            //通过zhy查出禽类
            List<PoultryCategory> poultryCategoryList = poultryCategoryService.getPoultryCategoryByCreate(barcodeMachine.getCreateBy());

            for (PoultryCategory p : poultryCategoryList) {
                log.debug(Thread.currentThread().getId() + "更新的禽类信息" + p.getName());
            }

            //将禽类信息封装成功字节数组
            List<byte[]> poultryCategoryBytesList = parsePoultryCategoryTobytes(poultryCategoryList);

            // 创建禽类品种信息更新数据包
            ByteBuf responseBuf = buildUpdateResponsePackage(deviceIdByte, (byte) 0x03, (short) 0, poultryCategoryBytesList);
            ctx.writeAndFlush(responseBuf);

        } else if (flag == 0x04) { // 设备程序更新信息

            log.info(Thread.currentThread().getId() + "Option：设备程序更新信息，设备编号：{}", deviceId);

            // 获取文件更新信息
            int fileSize = FileUtils.getFileSize(firmwareFilePath);
            if (fileSize == 0) {
                log.error(Thread.currentThread().getId() + "要更新的程序文件不存在或有损坏！请上传文件......");
            }

            int packageLength = (int) Math.ceil(fileSize / 1024);
            ProgramVersion programVersion = programVersionService.getProgramVersion();
            int firmwareVersion = programVersion.getFirmwareVersion();
            byte firmwareVersionByte = (byte) firmwareVersion;
            byte[] packageLengthBytes = ByteUtils.intToBytes(packageLength);
            byte[] fileSizeBytes = ByteUtils.intToBytes(fileSize);

            // 创建程序更新数据包
            ByteBuf responseBuf = buildProgramUpdateResponsePackage(deviceIdByte, (byte) 0x04, firmwareVersionByte, packageLengthBytes, fileSizeBytes);
            ctx.writeAndFlush(responseBuf);

        } else if (flag == 0x05) { // 去向市场扫码验证

            log.info(Thread.currentThread().getId() + "Option：去向市场扫码验证，设备编号：{}", deviceId);
//            byte[] barcodeNumber = new byte[20];
//            dataBuf.getBytes(11, barcodeNumber);
            byte[] barcodeNumber = new byte[16];
            dataBuf.getBytes(12, barcodeNumber);
            //String barcode = ByteUtils.bytesToASCIIString(barcodeNumber);
            String barcode = ByteUtils.bytesToAddress(barcodeNumber);
//            log.debug(Thread.currentThread().getId() + "二维码为   " + barcode);
            log.debug(Thread.currentThread().getId() + "卡号为   " + barcode);
            LabelingRecord labelingRecord = labelingRecordService.findByBarcodeNumber(barcode);

            //0x00 00(校验成功)
            //0x00 05(非法二维码)

            ByteBuf byteBuf;
            if (labelingRecord != null){
                byteBuf = buildHeartbeatResponsePackage(deviceIdByte, (byte) 0x05, new byte[]{0x00, 0x00});
                System.out.println(666);
            }
            else{
                System.out.println(555);
                byteBuf = buildHeartbeatResponsePackage(deviceIdByte, (byte) 0x05, new byte[]{0x00, 0x05});
            }
            //0x00 A0(二维码过期)

            // 响应设备数据
            ctx.writeAndFlush(byteBuf);

        } else if (flag == 0x06) { // 设备心跳数据

            log.info(Thread.currentThread().getId() + "Option：设备心跳数据，设备编号：{}", deviceId);

            // 获取设备状态与条码剩余量
            byte deviceStatus = dataBuf.getByte(11);
            short barcodeSurplusByte = dataBuf.getShort(12);

            log.debug(Thread.currentThread().getId() + "设备状态   " + deviceStatus);
            log.debug(Thread.currentThread().getId() + "条码剩余量   " + barcodeSurplusByte);

            // 保存设备状态信息 在线状态（离线：0、正常：1、卡纸：2、缺纸：3）
            if (deviceStatus == 0x00) { // 无故障，正常
                barcodeMachineStatusService.updateBarcodeMachineStatus(deviceId, "1", (int) barcodeSurplusByte);

            } else if (deviceStatus == 0x23) {  // 卡纸
                barcodeMachineStatusService.updateBarcodeMachineStatus(deviceId, "2", (int) barcodeSurplusByte);

                // 报警信息
                barcodeMachineAlertservice.addBarcodeMachineAlert("卡纸", deviceId, barcodeMachine.getCreateBy());

            } else if (deviceStatus == 0x24) { // 缺纸
                barcodeMachineStatusService.updateBarcodeMachineStatus(deviceId, "3", (int) barcodeSurplusByte);

                // 报警信息
                barcodeMachineAlertservice.addBarcodeMachineAlert("缺纸", deviceId, barcodeMachine.getCreateBy());

            }

            // 响应设备数据
            ByteBuf byteBuf = buildHeartbeatResponsePackage(deviceIdByte, (byte) 0x06, new byte[]{0x00, 0x00});
            ctx.writeAndFlush(byteBuf);

        } else if (flag == 0x07) { //数据上报

            log.info(Thread.currentThread().getId() + "Option：贴标数据上报，设备编号：{}", deviceId);

            // 获取上报数据列表
            int dataCount = (dataByte.length - 13) / 58;
            List<LabelingRecord> labelingRecordList = getCollectionDataList(dataBuf, dataCount, barcodeMachine);

            // 批量保存
            labelingRecordService.addbatch(labelingRecordList);

            // 响应设备数据
            ByteBuf byteBuf = buildHeartbeatResponsePackage(deviceIdByte, (byte) 0x07, new byte[]{0x00, dataReportCycle});
            ctx.writeAndFlush(byteBuf);

        } else if (flag == 0x08) {  // 获取禽类大类下的信息列表


            log.info(Thread.currentThread().getId() + "Option：获取禽类大类下的信息列表，设备编号：{}", deviceId);

            // 获取禽类品种大类编号
            byte poultryCategoryNumber = dataBuf.getByte(11);
            log.debug(Thread.currentThread().getId() + "获取禽类品种大类编号" + poultryCategoryNumber);

            // 获取大类编号下的子列表
            List<PoultryCategory> poultryCategory = poultryCategoryService.getPoultryCategoryByCreate(barcodeMachine.getCreateBy());
            List<byte[]> poultryCategoryBytesList = parsePoultryCategoryTobytes(poultryCategoryNumber, poultryCategory);

            // 创建禽类品种信息更新数据包
            ByteBuf responseBuf = buildUpdateResponsePackage(deviceIdByte, (byte) 0x08, (short) -1, poultryCategoryBytesList);
            ctx.writeAndFlush(responseBuf);


        } else if (flag == 0x09) {  // 开锁

            log.info(Thread.currentThread().getId() + "Option：开锁，设备编号：{}", deviceId);

            ByteBuf byteBuf;
            byte[] userIdByte = new byte[16];
            dataBuf.getBytes(13, userIdByte);
            String userId = ByteUtils.bytesToHexString(userIdByte);
            log.debug(Thread.currentThread().getId() + "开锁的userId  " + userId);

            User user = userService.findById(userId);
            //判断userId是否有idcard，有就说明被授权了，登录成功
            if (user != null && user.getIdCard() != null && !user.getIdCard().equals("")) {
                log.info(Thread.currentThread().getId() + "开锁成功");
                // 响应设备数据
                byteBuf = buildLoginResponsePackage(deviceIdByte, (byte) 0x09, new byte[]{0x00}, new byte[]{});
            } else {
                log.info(Thread.currentThread().getId() + "开锁失败");
                byteBuf = buildLoginResponsePackage(deviceIdByte, (byte) 0x08, new byte[]{0x01}, new byte[]{});
            }

            ctx.writeAndFlush(byteBuf);


        } else if (flag == 0x0a) {  // 设备获取固件包

            log.info(Thread.currentThread().getId() + "Option：固件包更新，设备编号：{}", deviceId);

            // 获取当前要下发的固件包信息
            int totalProgramPackage = dataBuf.getShort(12);
            int curProgramPackage = dataBuf.getShort(14);
            byte[] curProgramPackageBytes = new byte[2];
            dataBuf.getBytes(14, curProgramPackageBytes);

            // 固件版本信息获取
            ProgramVersion programVersion = programVersionService.getProgramVersion();
            int firmwareVersion = programVersion.getFirmwareVersion();
            byte firmwareVersionByte = (byte) firmwareVersion;

            // 获取固件包更新
            RandomAccessFile randomAccessFile = new RandomAccessFile(firmwareFilePath, "r");
            randomAccessFile.seek((curProgramPackage - 1) * 1024);

            // 开始发送文件
            log.info(Thread.currentThread().getId() + "正在发送文件，total：{}，seq：{}", totalProgramPackage, curProgramPackage);

            byte[] data = new byte[1024];
            if (randomAccessFile.read(data) != -1) {
                ByteBuf responseBuf = buildProgramUpdateResponsePackage(deviceIdByte, (byte) 0x0a, firmwareVersionByte, curProgramPackageBytes, data);
                ctx.writeAndFlush(responseBuf);
            }

            if (curProgramPackage == totalProgramPackage) {
                log.info(Thread.currentThread().getId() + "固件更新结束！设备编号：{}", deviceId);
            }

        }


    }


//    /**
//     * 数据读取完毕
//     *
//     * @param ctx
//     * @throws Exception
//     */
//    @Override
//    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
//        // 将服务端要写的消息刷新到远程结点，并关闭channel
//        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
//    }


    /**
     * 异常处理
     *
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        log.error(Thread.currentThread().getId() + "服务端异常,通道已关闭...");

        // 关闭通道
        cause.printStackTrace();
        ctx.close();
    }


    /**
     * 解析采集的贴标数据集合
     *
     * @param dataBuf        数据内容
     * @param dataCount      数据条数
     * @param barcodeMachine 脚标机信息
     * @return List<LabelingRecord>
     */
    private List<LabelingRecord> getCollectionDataList(ByteBuf dataBuf, int dataCount, BarcodeMachine barcodeMachine) {
        List<LabelingRecord> labelingRecordList = new ArrayList<>(dataCount);
        int headLen = 11;


        for (int i = 0; i < dataCount; i++) {
            LabelingRecord labelingRecord = new LabelingRecord();
            // 初始化贴标签的基本信息
            labelingRecord.setBarcodeMachineId(barcodeMachine.getNumber());
            labelingRecord.setStallsId(barcodeMachine.getStallsId());
            labelingRecord.setCreateDate(new Date());
            labelingRecord.setCreateBy(barcodeMachine.getCreateBy());


            labelingRecord.setId(idWorker.nextId() + "");

            // 条码信息
            byte[] barCodeBytes = new byte[16];
            dataBuf.getBytes(headLen + i * 58, barCodeBytes);
            String barCode = ByteUtils.bytesToASCIIString(barCodeBytes);
            labelingRecord.setBarcodeNumber(barCode);

            // 禽类信息 大类
            int poultryCategoryNumber = dataBuf.getInt(headLen + i * 58 + 24 + 2);
            labelingRecord.setPoultryCategoryId(poultryCategoryNumber + "");


            //11111111111欠家禽细类

            // 市场去向编号
            byte[] salesTerminalBytes = new byte[20];
            dataBuf.getBytes(headLen + i * 58 + 24 + 6, salesTerminalBytes);
            String salesTerminal = ByteUtils.bytesToASCIIString(salesTerminalBytes);
            labelingRecord.setSalesTerminalId(salesTerminal);

            // 日期
            byte[] dateBytes = new byte[6];
            dataBuf.getBytes(headLen + i * 58 + 24 + 6 + 20, dateBytes);
            Date date = DateUtils.hexBytesToDate(dateBytes);
            labelingRecord.setLabelingDate(date);

            labelingRecordList.add(labelingRecord);
        }

        return labelingRecordList;
    }


    /**
     * 校验 [收到的数据计算出来的CRC值] 与 [数据包中的CRC值]
     *
     * @param data 原缓冲区数据
     * @return 校验结果
     */
    private boolean checkCRCVal(byte[] data) {

        //如果被检测数据长度超过256，就只处理前256
        //修改为大于800字节
        int len = 0;
//        if ((data.length - 4) > 256)
        if ((data.length - 4) > 800)
            //len = 256;
            len = 800;
        else
            len = data.length - 4;


        byte[] checkData = new byte[len];
        System.arraycopy(data, 2, checkData, 0, len);

        if (doCheckCRCVal(checkData, data)) {
            System.out.println("crc验证成功");
            return true;
        }
        System.out.println("crc验证失败");
        return false;
    }


    /**
     * 对比数据CRC结果
     *
     * @param crcCheckData 待校验的数据：帧长度+帧代号+地址段+数据段
     * @param data         原来的数据包。
     * @return 一致则true, 否则false
     */
    private boolean doCheckCRCVal(final byte[] crcCheckData, final byte[] data) {
        int total = data.length;
        // 获取CRC校验位数据
        String t1 = ByteUtils.byteToHex(data[total - 2]);
        String t2 = ByteUtils.byteToHex(data[total - 1]);
        System.out.println(t1+t2+"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
        if (t1.length() < 2) {
            t1 = "0" + t1;
        }
        if (t2.length() < 2) {
            t2 = "0" + t2;
        }
        String originCrc = t1 + t2;

        String crc = CRC16.getCRC(crcCheckData);
        if (crc.equalsIgnoreCase(originCrc)) {
            return true;
        }

        log.error(Thread.currentThread().getId() + "校验不通过,原CRC校验码：" + originCrc + ",当前计算CRC校验码：" + crc);
        return false;
    }


    /**
     * 校验经纬度，正确返回true，错误返回false
     *
     * @param barcodeMachine 设备barcodeMachine
     * @param longitude      经度
     * @param latitude       纬度
     * @return boolean
     */
    private boolean checkGPS(BarcodeMachine barcodeMachine, byte[] longitude, byte[] latitude) {
        double longitudeVal = GPSUtils.dmParseDd(longitude);
        double latitudeVal = GPSUtils.dmParseDd(latitude);

        log.debug(Thread.currentThread().getId() + "测试-----经度  " + longitudeVal);
        log.debug(Thread.currentThread().getId() + "测试-----纬度  " + latitudeVal);

        // 查询当前脚标机所在位置
        double srcLongitude = Double.parseDouble(barcodeMachine.getLongitude());
        double srcLatitude = Double.parseDouble(barcodeMachine.getLatitude());

        // 是否在2km以内
        if (GPSUtils.isInRange2km(srcLongitude, srcLatitude, longitudeVal, latitudeVal)) {
            return true;
        }
        return false;
    }


    /**
     * 是否有新的禽类品种更新
     *
     * @param slaughterHouseId 屠宰场id
     * @return boolean
     */
    private boolean hasNewPoultry(String slaughterHouseId) {
        String isupdateByMachineId = slaughterHousePoultryService.getIsupdateByMachineId(slaughterHouseId);
        if ("1".equals(isupdateByMachineId)) {
            // 修改信息更新状态为未更新
            slaughterHousePoultryService.setIsupdateByMachineId(slaughterHouseId);
            return true;
        }
        return false;
    }
    /**
     * 是否有新的禽类品种更新
     *
     * @param slaughterHouseId 屠宰场id
     * @return boolean
     * @author minxin
     */
    private boolean hasNewPoultryTimeVersion(String timeVersion , String slaughterHouseId) {
        String time = slaughterHousePoultryService.getTimeVersionByMachineId(slaughterHouseId);
        return time.equals(timeVersion)?false:true;
    }

    /**
     * 判断当前的设备是否有新的用户更新
     *
     * @param deviceId 设备编号
     * @return boolean
     */
    private boolean hasNewUser(String deviceId) {
        // 查询脚标机用户信息表
        String isupdate = barcodeMachineUserService.getIsupdateByMachineId(deviceId);
        if ("1".equals(isupdate)) {
            // 修改信息更新状态为未更新
            barcodeMachineUserService.setIsupdateByMachineId(deviceId);
            return true;
        }
        return false;
    }
    /**
     * 判断当前设备的时间版本编号是否一致
     * @author cmx
     * @param timeVersion 时间版本编号
     * @param deviceId 设备编号
     */
    private boolean hasNewTimeVersion(String timeVersion,String deviceId) {
        // 查询脚标机用户信息表
        String time = barcodeMachineUserService.getTimeVersionByMachineId(deviceId);
        //相等返回是否需要更新 true为需要
        return timeVersion.equals(time)?false:true;
    }


    /**
     * 判断是否有固件进行更新
     *
     * @param firmwareVersion 设备端固件版本
     * @return boolean
     */
    private boolean hasNewFirmware(int firmwareVersion) {
        ProgramVersion programVersion = programVersionService.getProgramVersion();
        if (programVersion.getFirmwareVersion() == firmwareVersion) {
            return false;
        }
        return true;
    }


    /**
     * 将要更新的用户信息组装成字节数组信息
     *
     * @param barcodeMachineUser 脚标机的用户信息
     * @return List<byte       [       ]>
     * 用户信息集合：[用户级别 + 用户ID卡信息 + 预留信息]
     */
    private List<byte[]> parseUserInfoTobytes(BarcodeMachineUser barcodeMachineUser) throws UnsupportedEncodingException {
        String adminIdCard = barcodeMachineUser.getAdminIdCard();
        String[] splitIdCard = barcodeMachineUser.getMaintenanceIdCard().split(";");
        String userIdCard = barcodeMachineUser.getUserIdCard();
        List<byte[]> userdataList = new ArrayList<>();

        log.debug(Thread.currentThread().getId() + "adminIdCard    " + adminIdCard);
        log.debug(Thread.currentThread().getId() + "userIdCard   " + userIdCard);
        log.debug(Thread.currentThread().getId() + "maintenanceIdCard   " + Arrays.toString(splitIdCard));


        // 屠宰场管理员
        byte[] adminInfo = new byte[20];
        adminInfo[0] = 0x00;
//        byte[] adminIdCardBytes = ByteUtils.longToBytes(Long.parseLong(adminIdCard));
        byte[] adminIdCardBytes = ByteUtils.hexStringToBytes(adminIdCard);
        for (int i = adminIdCardBytes.length - 1, j = 16; i >= 0; i--, j--) {
            adminInfo[j] = adminIdCardBytes[i];
        }
        userdataList.add(adminInfo);


        // 屠宰场维护人员
        for (String idCard : splitIdCard) {
            byte[] maintenanceInfo = new byte[20];
            maintenanceInfo[0] = 0x01;
//            byte[] maintenanceIdCardBytes = ByteUtils.longToBytes(Long.parseLong(idCard));
//            byte[] maintenanceIdCardBytes = idCard.getBytes();
              byte[] maintenanceIdCardBytes = ByteUtils.hexStringToBytes(idCard);
            for (int i = maintenanceIdCardBytes.length - 1, j = 16; i >= 0; i--, j--) {
                maintenanceInfo[j] = maintenanceIdCardBytes[i];
            }
            userdataList.add(maintenanceInfo);
        }

        // 普通用户
        byte[] userInfo = new byte[20];
        userInfo[0] = 0x02;
//        byte[] userIdCardBytes = ByteUtils.longToBytes(Long.parseLong(userIdCard));
//        byte[] userIdCardBytes = userIdCard.getBytes();
        byte[] userIdCardBytes = ByteUtils.hexStringToBytes(userIdCard);
        for (int i = userIdCardBytes.length - 1, j = 16; i >= 0; i--, j--) {
            userInfo[j] = userIdCardBytes[i];
        }
        userdataList.add(userInfo);

        return userdataList;
    }


    /**
     * 将要更新的禽类品种信息组装成字节数组信息
     * 禽类品种大类编号：
     * 0x00鸡，0x01 鸭， 0x02 鹅 ，0x03 其他
     *
     * @param poultryCategoryList 禽类品种集合
     * @return List<byte   [   ]>
     */
    private List<byte[]> parsePoultryCategoryTobytes(List<PoultryCategory> poultryCategoryList) {

        List<byte[]> poultryCategoryBytesList = new ArrayList<>();

        for (PoultryCategory poultryCategory : poultryCategoryList) {

            if ("0".equals(poultryCategory.getNumber())) {  // 大类-鸡
                addpoultryCategoryBytes(poultryCategoryList, poultryCategoryBytesList, (byte) 0x00, poultryCategory.getId());

            } else if ("1".equals(poultryCategory.getNumber())) {   // 大类-鸭
                addpoultryCategoryBytes(poultryCategoryList, poultryCategoryBytesList, (byte) 0x01, poultryCategory.getId());

            } else if ("2".equals(poultryCategory.getNumber())) {   // 大类-鹅
                addpoultryCategoryBytes(poultryCategoryList, poultryCategoryBytesList, (byte) 0x02, poultryCategory.getId());

            } else if ("3".equals(poultryCategory.getNumber())) {   // 大类-其他
                addpoultryCategoryBytes(poultryCategoryList, poultryCategoryBytesList, (byte) 0x03, poultryCategory.getId());

            }

        }

        return poultryCategoryBytesList;
    }


    /**
     * 将指定要更新的大类品种下的子类信息信息组装成字节数组信息
     * 禽类品种大类编号：
     * 0x00鸡，0x01 鸭， 0x02 鹅 ，0x03 其他
     *
     * @param number              大类编号
     * @param poultryCategoryList 禽类品种集合
     * @return List<byte   [   ]>
     */
    private List<byte[]> parsePoultryCategoryTobytes(byte number, List<PoultryCategory> poultryCategoryList) {

        List<byte[]> poultryCategoryBytesList = new ArrayList<>();
        int num = number;
        String numStr = num + "";

        for (PoultryCategory poultryCategory : poultryCategoryList) {
            if (numStr.equals(poultryCategory.getNumber())) {
                addpoultryCategoryBytes(poultryCategoryList, poultryCategoryBytesList, number, poultryCategory.getId());
                break;
            }
        }

        return poultryCategoryBytesList;
    }


    /**
     * 将禽类信息添加到对应的字节数组集合中去
     *
     * @param poultryCategoryList      原禽类信息集合
     * @param poultryCategoryBytesList 要组装的禽类信息的集合，字节数组集合
     * @param parentNumber             大类品种id
     * @param categoryId               禽类品种的id
     */
    private void addpoultryCategoryBytes(List<PoultryCategory> poultryCategoryList, List<byte[]> poultryCategoryBytesList, byte parentNumber, String categoryId) {
        List<PoultryCategory> subCategory = getSubCategory(poultryCategoryList, categoryId);

        for (PoultryCategory sub : subCategory) {
            byte[] poultryCategoryByte = new byte[16];
            poultryCategoryByte[0] = 0x00;
            poultryCategoryByte[1] = parentNumber;

            byte[] subNumber = ByteUtils.intToBytes(Integer.parseInt(sub.getNumber()));
            byte[] subNameGbk = sub.getName().getBytes(Charset.forName("GBK"));

            System.arraycopy(subNumber, 0, poultryCategoryByte, 2, subNumber.length);
            System.arraycopy(subNameGbk, 0, poultryCategoryByte, 6, subNameGbk.length);
            //家禽名称：5个中文字符(10 Byte)，每个中文字GBK编号为2 Byte，家禽名称不足10 Byte的，加0xFF FF补齐到10 Byte;
            if(subNameGbk.length<10){
                for(int i=0;i<(10-subNameGbk.length);i++){
                    poultryCategoryByte[15-i]=(byte)0xff;
                }
            }
            poultryCategoryBytesList.add(poultryCategoryByte);
        }
    }

    /**
     * 获取大类品种下的小类信息
     *
     * @param poultryCategoryList 所有禽类品种信息列表
     * @param parentId            父级编号
     */
    private List<PoultryCategory> getSubCategory(List<PoultryCategory> poultryCategoryList, String parentId) {
        List<PoultryCategory> list = new ArrayList<>();

        for (PoultryCategory poultryCategory : poultryCategoryList) {
            if (poultryCategory.getParentId().equals(parentId)) {
                list.add(poultryCategory);
            }
        }

        return list;
    }


    /**
     * 构建设备登录响应设备的报文
     *
     * @param deviceId  设备编号
     * @param frameCode 帧代号
     * @param flag      标志位
     * @param data      响应数据内容
     * @return ByteBuf
     */
    private ByteBuf buildLoginResponsePackage(byte[] deviceId, byte frameCode, byte[] flag, byte[] data) {
        // 总长度
        int headLen = 11;   // 帧头 + 帧长度 + 帧代号 + 设备编号
        int footLen = 4;    // CRC校验 + 帧尾
        int len = headLen + 2 + data.length + footLen;
        byte[] dataByte = new byte[len];
        byte[] checkData = new byte[len - 6];
        ByteBuf buffer = Unpooled.buffer(len);

        // 响应数据
        buffer.writeBytes(new byte[]{(byte) 0x88, (byte) 0xFB});  // 帧头
        buffer.writeShort((len - 6));  // 帧长度（[帧代号]->[CRC]的字节数） 2个字节
        buffer.writeByte(frameCode);    // 帧代号
        buffer.writeBytes(deviceId);    // 设备编号
        buffer.writeBytes(flag);    // 标志位
        buffer.writeBytes(data);    // 数据内容

        // 获取CRC校验码
        buffer.getBytes(buffer.readerIndex(), dataByte);
        System.arraycopy(dataByte, 2, checkData, 0, checkData.length);
        byte[] crcBytes = CRC16.getCrcBytes(checkData);
        buffer.writeBytes(crcBytes);   // CRC校验数据
        buffer.writeBytes(new byte[]{(byte) 0xFC, (byte) 0xFC});    // 帧尾

        return buffer;
    }


    /**
     * 构建信息更新响应设备的报文
     *
     * @param deviceId      设备编号
     * @param frameCode     帧代号
     * @param updateVersion 更新版本
     * @param dataList      响应数据内容集合
     * @return ByteBuf
     * 00 1b 08 03 03 03 03 03 03 00 00 00 00 03 e9 c7 e5 d4 b6 bc a6 ff ff ff ff 00 00
     * 00 1b 08 03 03 03 03 03 03 00 00 00 00 03 e9 c7 e5 d4 b6 bc a6 ff ff ff ff
     */
    private ByteBuf buildUpdateResponsePackage(byte[] deviceId, byte frameCode, short updateVersion, List<byte[]> dataList) {
        if (dataList.size() == 0)
            return Unpooled.EMPTY_BUFFER;

        // 总长度
        int headLen = 11;   // 帧头 + 帧长度 + 帧代号 + 设备编号  2+2+1+6
        int footLen = 4;    // CRC校验 + 帧尾  2+2
        int len = headLen  + 2 + dataList.size() * dataList.get(0).length + footLen;  //这里的2是用户时间版本的长度
        //如果是第八条指令的话len需要减去二，因为没有长度为二的时间序列长度
        if(frameCode==(byte)0x08){
            len-=2;
        }

        byte[] dataByte = new byte[len];
        byte[] checkData = new byte[len - 6];

        ByteBuf buffer = Unpooled.buffer(len);

        // 响应数据
        buffer.writeBytes(new byte[]{(byte) 0x88, (byte) 0xFB});  // 帧头
        buffer.writeShort((len - 6));  // 帧长度（[帧代号]->[CRC]的字节数） 2个字节
        buffer.writeByte(frameCode);    // 帧代号          1字节
        buffer.writeBytes(deviceId);    // 设备编号     6字节
        if (updateVersion != -1)
            buffer.writeShort(updateVersion);    // 信息时间版本号  2字节

        // 更新信息数据内容
        for (byte[] dataInfo : dataList) {
            buffer.writeBytes(dataInfo);
        }

        // 获取CRC校验码  0,
        buffer.getBytes(buffer.readerIndex(), dataByte);

//        System.out.println(ByteUtils.bytesToHexString(dataList));
        System.out.println("dataByte为："+ByteUtils.bytesToHexString(dataByte));

        System.arraycopy(dataByte, 2, checkData, 0, checkData.length);

        System.out.println("checkData为："+ByteUtils.bytesToHexString(checkData));

        byte[] crcBytes = CRC16.getCrcBytes(checkData);

        System.out.printf("crc校验码为：");
        for (byte a: crcBytes
             ) {
            System.out.printf(ByteUtils.byteToHex(a)+" ");
        }

        buffer.writeBytes(crcBytes);   // CRC校验数据
        buffer.writeBytes(new byte[]{(byte) 0xFC, (byte) 0xFC});    // 帧尾

        return buffer;
    }


    /**
     * 构建心跳数据响应设备的报文
     *
     * @param deviceId  设备编号
     * @param frameCode 帧代号
     * @param data      响应数据内容集合
     * @return ByteBuf
     */
    private ByteBuf buildHeartbeatResponsePackage(byte[] deviceId, byte frameCode, byte[] data) {

        // 总长度
        int headLen = 11;   // 帧头 + 帧长度 + 帧代号 + 设备编号
        int footLen = 4;    // CRC校验 + 帧尾
        int len = headLen + data.length + footLen;
        byte[] dataByte = new byte[len];
        byte[] checkData = new byte[len - 6];
        ByteBuf buffer = Unpooled.buffer(len);

        // 响应数据
        buffer.writeBytes(new byte[]{(byte) 0x88, (byte) 0xFB});  // 帧头
        buffer.writeShort((len - 6));    // 帧长度（[帧代号]->[CRC]的字节数） 2个字节
        buffer.writeByte(frameCode);    // 帧代号
        buffer.writeBytes(deviceId);    // 设备编号
        buffer.writeBytes(data);    // 数据

        // 获取CRC校验码
        buffer.getBytes(buffer.readerIndex(), dataByte);
        System.arraycopy(dataByte, 2, checkData, 0, checkData.length);
        byte[] crcBytes = CRC16.getCrcBytes(checkData);
        buffer.writeBytes(crcBytes);   // CRC校验数据
        buffer.writeBytes(new byte[]{(byte) 0xFC, (byte) 0xFC});    // 帧尾

        return buffer;
    }


    /**
     * 构建程序更新信息响应数据包
     *
     * @param deviceId             设备编号
     * @param frameCode            帧代号
     * @param firmwareVersionBytes 固件版本号
     * @param packageLengthBytes   固件包的大小 KB为单位
     * @param fileSizeBytes        固件长度
     * @return ByteBuf
     */
    private ByteBuf buildProgramUpdateResponsePackage(byte[] deviceId, byte frameCode, byte firmwareVersionBytes, byte[] packageLengthBytes, byte[] fileSizeBytes) {
        // 总长度
        int headLen = 11;   // 帧头 + 帧长度 + 帧代号 + 设备编号
        int footLen = 4;    // CRC校验 + 帧尾
        int len = headLen + 1 + packageLengthBytes.length + fileSizeBytes.length + footLen;
        byte[] dataByte = new byte[len];
        byte[] checkData = new byte[len - 6];
        ByteBuf buffer = Unpooled.buffer(len);

        // 响应数据
        buffer.writeBytes(new byte[]{(byte) 0x88, (byte) 0xFB});  // 帧头
        buffer.writeShort((len - 6));  // 帧长度（[帧代号]->[CRC]的字节数） 2个字节
        buffer.writeByte(frameCode);    // 帧代号
        buffer.writeBytes(deviceId);    // 设备编号
        buffer.writeByte(firmwareVersionBytes); // 固件版本
        buffer.writeBytes(packageLengthBytes);  // 固件包大小
        buffer.writeBytes(fileSizeBytes);   // 数据

        // 获取CRC校验码
        buffer.getBytes(buffer.readerIndex(), dataByte);
        System.arraycopy(dataByte, 2, checkData, 0, checkData.length);
        byte[] crcBytes = CRC16.getCrcBytes(checkData);
        buffer.writeBytes(crcBytes);   // CRC校验数据
        buffer.writeBytes(new byte[]{(byte) 0xFC, (byte) 0xFC});    // 帧尾

        return buffer;
    }


}
